/*
 * Copyright (C) 2015-2019 Alibaba Group Holding Limited
 */


#include <stdio.h>

#include "ulog/ulog.h"
#include "aos/kernel.h"
#include "canopen/canopen.h"
#include "board.h"
#include "canopen/io_board_api.h"
#include "canopen/co_io.h"

#include "co_master_config.h"
#include "co_master_core.h"
#include "co_master.h"

static io_board_state_change_cb_t io_board_state_change_cb = NULL;

static io_board_input_change_cb_t input_change_cb          = NULL;

static void can_io_input_change(const uint8_t can_port, const uint8_t node, const uint8_t input_level_size, const uint8_t input_level[8]);

static void can_io_state_change(const uint8_t can_port, const uint8_t node, const io_board_state_t state);

void component_upgrade(const can_node_id_t *node, const uint8_t response)
{
    if (NULL != node) {
        LOGI(CO_MASTER_LOG_TAG, "Node (0x%x) State %d", node->node, response);
    }
}

int32_t io_board_manager_init(io_board_state_change_cb_t _io_board_state_change, io_board_input_change_cb_t _input_change_cb)
{
    const uint8_t node_cnt = get_io_node_size();
    uint8_t node_list[node_cnt];
    uint8_t i = 0;
    uint8_t co_io_node = INVALID_NODE;

    co_master_core_init();

    while (INVALID_NODE != (co_io_node=foreach_can_io_node(&i))) {
        node_list[i] = co_io_node;
        i++;
    }
    io_board_state_change_cb = _io_board_state_change;
    input_change_cb = _input_change_cb;

    if (0 != co_master_init(PORT_CAN_IO, node_list, node_cnt, can_io_state_change, can_io_input_change)) {
        LOGE(CO_MASTER_LOG_TAG, "co master init fail");
    }

    co_master_notify(co_master_core_reset_all, 0, NULL);

    return 0;
}

/*int32_t gpio_output(const uint8_t gpio_number, const bool set)
{
    int32_t rc = -1;
    uint8_t i  = 0;
    co_io_table_list_t *list = NULL;
    uint16_t off_set = 0;
    while (NULL != (list = foreach_io_board_table(&i))) {
        if(list->state==io_board_working){
            if( gpio_number<(off_set+list->output_cnt) ) {
                rc = gpio_set_output(PORT_CAN_IO, list->node, gpio_number-off_set, set);
                break;
            }else{
                off_set+=list->output_cnt;
            }
        } else {
            LOGE(CO_MASTER_LOG_TAG, "Operate Abort as node %d state %d", list->node, list->state);
            break;
        }

        i++;
    }
    return rc ;
}

uint16_t get_gpio_input_cnt(void)
{
    uint8_t i  = 0;
    co_io_table_list_t *list = NULL;
    uint16_t rc = 0;

    while (NULL != (list = foreach_io_board_table(&i))) {
        if(list->state==io_board_working){
            rc+=list->output_cnt;
        } else {
            LOGE(CO_MASTER_LOG_TAG, "Operate Abort as node %d state %d", list->node, list->state);
            break;
        }

        i++;
    }
    return rc ;
}

uint16_t get_gpio_output_cnt(void)
{
    uint8_t i  = 0;
    co_io_table_list_t *list = NULL;
    uint16_t rc = 0;

    while (NULL != (list = foreach_io_board_table(&i))) {
        if(list->state==io_board_working){
            rc+=list->input_cnt;
        } else {
            LOGE(CO_MASTER_LOG_TAG, "Operate Abort as node %d state %d", list->node, list->state);
            break;
        }

        i++;
    }
    return rc ;
}

*/

int32_t gpio_output(const uint8_t node, const uint8_t gpio_number, const bool set)
{
    int32_t rc = -1;

    co_io_table_list_t *list = get_io_node(node);
    if(NULL != list) {
        if(io_board_working == list->state && gpio_number<list->output_cnt) {
            rc = gpio_set_output(PORT_CAN_IO, node, gpio_number, set);
        } else {
            LOGE(CO_MASTER_LOG_TAG, "Operate Abort as node %d state %d number %d", list->node, list->state, list->output_cnt);
        }
    }

    return rc;
}

uint16_t get_io_board_input_cnt(const uint8_t node)
{
    uint16_t rc = -1;

    co_io_table_list_t *list = get_io_node(node);
    if(NULL != list) {
        if(io_board_working == list->state) {
            rc = list->input_cnt;
        }
    }
    return rc ;
}

uint16_t get_io_board_output_cnt(const uint8_t node)
{
    uint16_t rc = -1;

    co_io_table_list_t *list = get_io_node(node);
    if(NULL != list) {
        if(io_board_working == list->state) {
            rc = list->output_cnt;
        }
    }
    return rc ;
}

int32_t inquire_gpio_output(const uint8_t board_id, const uint8_t gpio_number)
{
    int32_t rc = -1;

    co_io_table_list_t *list = get_io_node(board_id);
    if(NULL != list) {
        if(io_board_working == list->state && gpio_number<list->output_cnt) {
            rc = (list->output_actual_value[gpio_number/8] & (1<<(gpio_number%8)))>>(gpio_number%8);
        }
    }
    return rc ;
}

int32_t inquire_gpio_input(const uint8_t board_id, const uint8_t gpio_number)
{
    int32_t rc = -1;

    co_io_table_list_t *list = get_io_node(board_id);
    if(NULL != list) {
        if(io_board_working == list->state && gpio_number<list->output_cnt) {
            rc = (list->input_actual_value[gpio_number/8] & (1<<(gpio_number%8)))>>(gpio_number%8);
        }
    }
    return rc ;
}

/* 批量获取指定IO扩展板的24个输入口状态，add by maoxinfeng */
int32_t inquire_gpio_input_batch(const uint8_t board_id, uint32_t *data, const uint16_t start_number, const uint16_t count)
{
    int32_t rc = 0;
    int pin_num=0;
    unsigned int bit;

    co_io_table_list_t *list = get_io_node(board_id);
    if(NULL != list) {
        if(io_board_working == list->state) {
            for(int i=0;i<count;i++){
                pin_num=start_number+i;
                if(pin_num>=list->input_cnt){break;}
                bit=(list->input_actual_value[pin_num/8] & (1<<(pin_num%8)))>>(pin_num%8);
                if(bit==0){
                    *data&=(~(0x00000001<<pin_num));
                }
                else if(bit==1){
                    *data|=(0x00000001<<pin_num);
                }
            }
            *data>>=start_number;
        }else {
            *data = 0xFFFFFFFF;
            return -2;
        }
    }else {
        *data = 0xFFFFFFFF;
        return -1;
    }
    return 0 ;
}

int32_t inquire_io_board_version(const uint8_t board_id, char *buffer, const uint16_t buffer_len)
{
    int32_t rc = -1;
    if(NULL != buffer && buffer_len>1) {

        co_io_table_list_t *list = get_io_node(board_id);
        if(NULL != list) {
            if(NULL != list->version) {
               strncpy(buffer, list->version, buffer_len-1);
               rc = 0;
          }
        }
    }
    return rc ;
}

int32_t set_gpio_output_batch(uint8_t board_id, uint8_t *data, const uint16_t start_number, const uint16_t count)
{
    int32_t rc = -1;

    if(NULL != data){
        co_io_table_list_t *io_table_node = get_io_node(board_id);

    if (NULL != io_table_node && (start_number+count) <= io_table_node->output_cnt) {
        uint8_t bit_pos = start_number;
        for(; bit_pos<(count+start_number); bit_pos++) {
            io_table_node->output_value[bit_pos / 8] &= ~(1 << (bit_pos % 8));
            io_table_node->output_value[bit_pos / 8] |= (data[bit_pos / 8]&(1 << (bit_pos % 8)));
        }

        pdo_tx_set_t pdo_tx_data = { io_table_node->output_cnt / 8 + ((io_table_node->output_cnt % 8 == 0) ? 0 : 1), 0, io_table_node->output_value };
        rc = pdo_set(PORT_CAN_IO, board_id, GPIO_OUT_RXPDO_IDX, &pdo_tx_data, true);
    }
    }
    return rc;

}

int32_t set_gpio_output_default(const uint8_t board_id, const uint8_t len, const uint8_t *data)
{
    int32_t rc = -1;
    if(NULL != data) {
        can_node_id_t node_id = { PORT_CAN_IO, board_id };
        rc = co_sdo_write_od(&node_id, CO_OUTPUT_INDEX, CO_OUTPUT_SUB_INDEX_DEFAULT, data, len);
        if(sdo_transfer_ok != rc) {
            LOGE(CO_MASTER_LOG_TAG, "board %d set gpio output default failed %d", rc);
        }
    }

    return rc;
}

int32_t get_gpio_output_default(const uint8_t board_id, const uint8_t buf_len, uint8_t *buf)
{
    int32_t rc = -1;
    if(NULL != buf) {
        co_io_table_list_t *io_node = get_io_node(board_id);
        if(NULL != io_node && buf_len>=sizeof(io_node->output_def_value)) {
            memcpy(buf, io_node->output_def_value, sizeof(io_node->output_def_value));
            rc = 0;
        }
    }

    return rc;
}


static void can_io_input_change(const uint8_t can_port, const uint8_t node, const uint8_t input_level_size, const uint8_t input_level[8])
{
    LOGI(CO_MASTER_LOG_TAG, "Port %d Node %d GPIO Input Change, Byte Count %d, Value [0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x]",
        can_port, node, input_level_size, input_level[0], input_level[1], input_level[2], input_level[3]
        , input_level[4], input_level[5], input_level[6], input_level[7]);
    if(PORT_CAN_IO==can_port && NULL != input_change_cb) {
        input_change_cb(node, input_level_size, input_level);
    }
}

static void can_io_state_change(const uint8_t can_port, const uint8_t node, const io_board_state_t state)
{
    LOGI(CO_MASTER_LOG_TAG, ">>> Port %d Node %d State %d", can_port, node, state);
    if(NULL != io_board_state_change_cb && PORT_CAN_IO==can_port) {
        co_io_table_list_t *io_node=get_io_node(node);
        if(NULL !=io_node ) {
            io_node->state = state;
            io_board_state_change_cb(node, state);
        }
    }
}

